import socket
import threading
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
# 帮我们直接运行此文件时，可以加载到上级目录的ui包
sys.path.append("../")

from common import utils
from ui.Ui_chat_room_dialog import Ui_Dialog
import json

"""
# 1. 创建一个线程，用来连接服务器（避免卡死界面）

# 2. Input 在子线程接收服务器发送的消息
    # ● 接收群通知
    # ● 接收群成员列表
    # ● 接受并展示发来的聊天消息
    
# 3. Output 将昵称发送给服务器
    # ● 获取用户输入的数据，发送给服务器
"""

CODE_MSG_HISTORY = 0
CODE_MSG_NEW     = 1
CODE_CLIENT_LIST = 2
CODE_NOTIFY      = 4

class ChatRoomDialog(QDialog):
    
    #a. 创建一个信号，用来发送消息
    on_recv_msg_signal = pyqtSignal(int, object)

    def __init__(self, parent = None, room_ip = "", room_port = 0, nickname = "匿名"):
        super().__init__(parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        
        self.tcp_client: socket.socket = None
        
        self.room_ip = room_ip
        self.room_port = room_port
        self.nickname = nickname
        print("加入房间：", room_ip, room_port, nickname)
        self.members_model = QStringListModel()
        
        # c. 连接信号和槽函数
        self.on_recv_msg_signal.connect(self.on_msg_recv)
        
        self.init_ui()
        self.init_data()
        
        # 1. 创建一个线程，用来连接服务器（避免卡死界面）
        threading.Thread(target=self.connect_server, daemon=True).start()
            
        # 3. Output 将昵称发送给服务器
        # ● 获取用户输入的数据，发送给服务器
        
    # b. 创建一个槽函数，用来接收消息
    def on_msg_recv(self, code, data):
        # print("收到消息：", code, data)
        # 回到主线程，更新UI
        if code == CODE_NOTIFY:
            # 4. 接收群通知
            self.ui.edit_notify.clear()
            self.ui.edit_notify.appendPlainText(data["notice"])
            self.setWindowTitle("聊天室 - 【{}】{}:{}".format(data["name"], data["ip"], data["port"]))
        elif code == CODE_CLIENT_LIST:
            client_list = [ f"{item[0]} \t ({item[1][0]}:{item[1][1]})" for item in data]
            self.members_model.setStringList(client_list)
        elif code == CODE_MSG_HISTORY:
            self.init_msg(data)
        elif code == CODE_MSG_NEW:
            self.append_msgs(data)
            
    def connect_server(self):
        
        # 1. 根据TCP服务器的ip和端口，创建socket连接到服务器
        self.tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            # 设置端口复用
            self.tcp_client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 设置连接超时时间2s
            self.tcp_client.settimeout(2)
            # 连接服务器
            self.tcp_client.connect((self.room_ip, self.room_port))
            print("连接服务器成功")
            # 设置连接超时时间为None
            self.tcp_client.settimeout(None)
            # 将昵称发送给服务器
            self.tcp_client.send(self.nickname.encode())
            
            # 2. 接收服务器发送的消息
            while True:
                # 接收服务器发送的消息
                data = self.tcp_client.recv(1024 * 8)
                if data:
                    msg = utils.decode_data(data)
                    # 解析服务器发送的消息
                    json_data = json.loads(msg)
                    # 分别获取code, msg, data
                    code = json_data["code"]
                    msg = json_data["msg"]
                    data = json_data["data"]
                    print("==> 收到服务器的消息：",code, msg)
                                
                    # 2. Input 在子线程接收服务器发送的消息
                        # ● 接收群通知
                        # ● 接收群成员列表
                        # ● 接受并展示发来的聊天消息
                    # d. 发送信号，将消息传递给槽函数
                    # self.on_msg_recv(code, data)
                    self.on_recv_msg_signal.emit(code, data)
                else:
                    print("服务器断开连接")
                    break
        except Exception as e:
            print("网络连接异常", e)
        finally:
            if self.tcp_client is not None:
                self.tcp_client.close()
                self.tcp_client = None


    def init_msg(self, msgs):
        self.ui.edit_msg_list.clear()
        for msg in msgs:
            self.append_msg(msg)
            
    def append_msgs(self, msgs):
        for msg in msgs:
            self.append_msg(msg)

    def append_msg(self, msg):
        # 给消息列表填充假数据
        self.ui.edit_msg_list.appendHtml(
            f"<font color='blue'> {msg['nickname']} </font> "
            f"<font color='gray'> {msg['from']} </font> "
            f"<font color='#c100db'> {msg['time']} </font>"
        )
        
        #  添加黑色消息内容
        self.ui.edit_msg_list.appendHtml(f"<font color='black'>{msg['message']}</font>")
        self.ui.edit_msg_list.appendPlainText("")
        
    def send_msg(self, msg):
        # 判断是否连接到服务器
        if self.tcp_client is None:
            print("未连接到服务器")
            return
        
        # json.dumps({"code": CODE_MSG_NEW, "msg": msg})
        self.tcp_client.send(msg.encode())
        
        
    @pyqtSlot()
    def on_btn_send_clicked(self):
        # ● 获取用户输入的数据，发送给服务器
        # 获取用户输入的数据
        msg = self.ui.edit_send.toPlainText()
        if not msg:
            print("消息不能为空")
            QMessageBox.warning(self, "警告", "消息不能为空")
            return
        
        print("发送消息：", msg)
        # # 发送消息给服务器
        self.send_msg(msg)
        self.ui.edit_send.clear()
        
    @pyqtSlot()
    def on_btn_close_clicked(self):
        self.close()
        
    def init_ui(self):
        self.ui.lv_members.setModel(self.members_model)
        # 禁止用户编辑lv_members
        self.ui.lv_members.setEditTriggers(QAbstractItemView.NoEditTriggers)

    def init_data(self):
        self.ui.edit_notify.appendPlainText("<畅所欲言，友善交流>")
        self.ui.edit_notify.appendPlainText("聊天室地址：{}:{}".format(self.room_ip, self.room_port))
        # 给消息列表填充假数据
        msg = {"nickname": "系统消息", "from": ["192.168.40.213", 8888], "time": "2024年9月14日 09点54分23秒", "message": "欢迎加入聊天室"}
        self.append_msg(msg)
        
        self.ui.edit_msg_list.appendHtml(
            "<font color='blue'>老王</font>"
            "<font color='gray'>(['192.168.40.123', 8888])</font>"
            "<font color='#c100db'>2024年9月14日 09点54分23秒</font>"
        )
        # 添加黑色消息内容
        self.ui.edit_msg_list.appendHtml("<font color='black'>哈喽，大家好</font>")
        self.ui.edit_msg_list.appendPlainText("")
        
        # 给QListView添加假数据
        self.members_model.setStringList([
            "张三(192.168.31.100)",
            "里斯(192.168.31.101)",
            "王五(192.168.31.102)",
            "赵六(192.168.31.103)",
        ])
        

if __name__ == '__main__':
    app = QApplication(sys.argv)

    window = ChatRoomDialog(
        room_ip = "192.168.40.55", room_port = 8001, nickname = "匿名"
    )
    window.show()

    sys.exit(app.exec_())